#include "elm.h"
#include "time.h"
#include "math.h"
#include <pseudoinverse.h>
ELM::ELM(uint inputNodeCnt, uint outputNodeCnt, uint hiddenNodeCnt):
    mInputNodeCnt(inputNodeCnt),
    mOutputNodeCnt(outputNodeCnt),
    mHiddenNodeCnt(hiddenNodeCnt)
{

    mSampleSize=0;
    mWeight=new double*[mInputNodeCnt];
    for(uint i=0;i<mInputNodeCnt;i++){
        mWeight[i]=new double[mHiddenNodeCnt];
    }
    mBias=new double[mHiddenNodeCnt];
    mInput=new double*[mInputNodeCnt];
    for(uint i=0;i<mInputNodeCnt;i++)
    {
        mInput[i]=nullptr;
    }
    mOutput=new double*[mOutputNodeCnt];
    for(uint i=0;i<mOutputNodeCnt;i++)
    {
        mOutput[i]=nullptr;
    }

    mTestOutput=new double[mOutputNodeCnt];

}

ELM::~ELM()
{
    if(mWeight!=nullptr){
        for(uint i=0;i<mInputNodeCnt;i++){
            delete[] mWeight[i];
        }
        delete[] mWeight;
    }
    if(mBias!=nullptr){
        delete[] mBias;
    }
    for(uint i=0;i<mInputNodeCnt;i++)
    {
        if(mInput[i]!=nullptr)
            delete[] mInput[i];
    }
    delete[] mInput;
    for(uint i=0;i<mOutputNodeCnt;i++)
    {
        if(mOutput[i]!=nullptr)
            delete[] mOutput[i];
    }
    delete[] mOutput;
    delete[] mTestOutput;
}

uint ELM::sampleSize() const
{
    return mSampleSize;
}

void ELM::setSampleSize(const uint &sampleSize)
{
    mSampleSize = sampleSize;

}

void ELM::randomWeightAndBias()
{
    srand(static_cast<uint>(time(nullptr)));
    for(uint i=0;i<mHiddenNodeCnt;i++){
        for(uint j=0;j<mInputNodeCnt;j++)
        {
            mWeight[j][i]=rand()%10000/10000.0;
        }
        mBias[i]=rand()%10000/10000.0;
    }
}

bool ELM::train()
{

    mH.resize(static_cast<int>(mSampleSize),static_cast<int>(mHiddenNodeCnt));
    mT.resize(static_cast<int>(mSampleSize),static_cast<int>(mOutputNodeCnt));
    for(uint i=0;i<mSampleSize;i++)
    {
        for(uint j=0;j<mHiddenNodeCnt;j++)
        {
            double temp=0;
            for(uint k=0;k<mInputNodeCnt;k++)
            {
                temp+=mInput[k][i]*mWeight[k][j];
            }
            temp+=mBias[j];
            mH[static_cast<int>(i)][static_cast<int>(j)]=sigmoid(temp);
        }
        for(uint j=0;j<mOutputNodeCnt;j++)
        {
            mT[static_cast<int>(i)][static_cast<int>(j)]=mOutput[j][i];
        }
    }

    mBeta=pinv(mH)*mT;

    return true;
}

void ELM::setInput(uint num, double *data)
{
    if(num<mInputNodeCnt){
        if(mInput[num]!=nullptr)
            delete[] mInput[num];
        mInput[num]=data;
    }
}

void ELM::setOutput(uint num, double *data)
{
    if(num<mOutputNodeCnt){
        if(mOutput[num]!=nullptr)
            delete[] mOutput[num];
        mOutput[num]=data;
    }
}

void ELM::test(double *sample)
{

    mTestH.resize(1,static_cast<int>(mHiddenNodeCnt));
    for(uint i=0;i<mHiddenNodeCnt;i++)
    {
        double temp=0;
        for(uint k=0;k<mInputNodeCnt;k++)
        {
            temp+=sample[k]*mWeight[k][i];
        }
        temp+=mBias[i];
        mTestH[0][i]=sigmoid(temp);
    }
    mTestT=mTestH*mBeta;
    for(uint i=0;i<mOutputNodeCnt;i++)
    {
        mTestOutput[i]=mTestT[0][i];
    }
}

double *ELM::testOutput() const
{
    return mTestOutput;
}

double ELM::testOutput(uint num)
{
    if(mTestOutput!=nullptr&&num<mSampleSize){
        return mTestOutput[num];
    }
    return 0;
}

double ELM::sigmoid(double v)
{
    return 1/(1+exp(-v));
}
